﻿#region Using

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using VkAPIAsync.Wrappers.Common;
using VkAPIAsync.Wrappers.Users;

#endregion

namespace Vk.Methods.Users
{
    /// <summary>
    ///     Пользователи
    /// </summary>
    public class Users
    {
        private readonly API _api;

        public Users(API api)
        {
            _api = api;
        }

        /// <summary>
        ///     Возвращает расширенную информацию о пользователях.
        /// </summary>
        /// <param name="uids">
        ///     Перечисленные через запятую ID пользователей или их короткие имена (screen_name). Максимум 1000
        ///     пользователей.
        /// </param>
        /// <param name="fields">
        ///     Перечисленные через запятую поля анкет, необходимые для получения. Доступные значения: userId,
        ///     first_name, last_name, nickname, screen_name, sex, bdate (birthdate), city, country, timezone, photo, photo_medium,
        ///     photo_big, has_mobile, rate, contacts, education, online, counters.
        /// </param>
        /// <param name="nameCase">
        ///     Падеж для склонения имени и фамилии пользователя. Возможные значения: именительный – nom,
        ///     родительный – gen, дательный – dat, винительный – acc, творительный – ins, предложный – abl. По умолчанию nom.
        /// </param>
        public async Task<List<User>> Get(IEnumerable<string> uids = null, IEnumerable<string> fields = null,
            string nameCase = null)
        {
            var query = new ApiQuery("users.get", true);
            if (nameCase != null)
            {
                query.Add("name_case", nameCase);
            }
            if (fields != null)
            {
                query.Add("fields", fields.Aggregate((str, cur) => str + "," + cur));
            }
            if (uids != null)
            {
                query.Add("user_ids", uids.Aggregate((a, b) => a + "," + b));
            }

            var result = await _api.Manager.Execute(query);
            if (_api.Manager.MethodSuccessed)
            {
                if (!result.ChildNodes.Any())
                {
                    return null;
                }
                return result.ChildNodes.Select(node => new User(node)).ToList();
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список пользователей в соответствии с заданным критерием поиска.
        /// </summary>
        /// <param name="query">Cтрока поискового запроса. Например, Вася Бабич.</param>
        /// <param name="fields">
        ///     Перечисленные через запятую поля анкет, необходимые для получения. Доступные значения: userId,
        ///     first_name, last_name, nickname, screen_name, sex, bdate (birthdate), city, country, timezone, photo, photo_medium,
        ///     photo_big, has_mobile, rate, contacts, education, online.
        /// </param>
        /// <param name="count">Количество возвращаемых пользователей (максимум 1000). По умолчанию 20.</param>
        /// <param name="offset">Смещение относительно первого найденного пользователя для выборки определенного подмножества.</param>
        /// <param name="ageFrom">Начиная с какого возраста. </param>
        /// <param name="ageTo">До какого возраста.</param>
        /// <param name="birthDay">День рождения. </param>
        /// <param name="birthMonth">Месяц рождения. </param>
        /// <param name="birthYear">Год рождения. </param>
        /// <param name="city">Идентификатор города. </param>
        /// <param name="company">Название компании, в которой работают пользователи. </param>
        /// <param name="country">Идентификатор страны.</param>
        /// <param name="groupId">Идентификатор группы, среди пользователей которой необходимо проводить поиск. </param>
        /// <param name="hasPhoto">Только с фотографией</param>
        /// <param name="hometown">Название города строкой</param>
        /// <param name="interests">Интересы.</param>
        /// <param name="isOnline">Только в сети</param>
        /// <param name="position">Название должности.</param>
        /// <param name="schoolClass">Идентификатор школьного класса.</param>
        /// <param name="religion">Религиозные взгляды.</param>
        /// <param name="school">Идентификатор школы, которую закончили пользователи.</param>
        /// <param name="schoolCity">Идентификатор города, в котором пользователи закончили школу. </param>
        /// <param name="schoolCountry">Идентификатор страны, в которой пользователи закончили школу.</param>
        /// <param name="schoolYear">Год окончания школы.</param>
        /// <param name="universityChair">Идентификатор кафедры.</param>
        /// <param name="sex">Пол</param>
        /// <param name="sort">Сортировка результатов</param>
        /// <param name="status">Семейное положение</param>
        /// <param name="university">Идентификатор ВУЗа.</param>
        /// <param name="universityCountry">Идентификатор страны, в которой пользователи закончили ВУЗ.</param>
        /// <param name="universityYear">Год окончания ВУЗа.</param>
        /// <param name="universityFaculty">Идентификатор факультета. </param>
        /// <param name="fromList">Разделы среди которых нужно осуществить поиск, перечисленные через запятую: 
        ///                        friends – искать среди друзей, 
        ///                        subscriptions – искать среди друзей и подписок пользователя
        /// </param>
        public async Task<ListCount<User>> Search(string query = null, IEnumerable<string> fields = null,
            int? count = null, int? offset = null, UsersSortOrder sort = null, int? city = null, int? country = null,
            string hometown = null, int? universityCountry = null, int? university = null, int? universityYear = null,
            int? universityFaculty = null, int? universityChair = null,
            UserSex sex = null, UserMaritalStatus status = null, int? ageFrom = null, int? ageTo = null,
            int? birthDay = null, int? birthMonth = null, int? birthYear = null,
            bool? isOnline = null, bool? hasPhoto = null, int? school = null, int? schoolCountry = null,
            int? schoolCity = null, int? schoolYear = null, int? schoolClass = null,
            string religion = null, string interests = null, string company = null, string position = null,
            int? groupId = null, IEnumerable<string> fromList = null)
        {
            var apiQuery = new ApiQuery("users.search");

            if (query != null)
            {
                apiQuery.Add("q", query);
            }
            if (fields != null)
            {
                apiQuery.Add("fields", fields);
            }
            if (count.HasValue)
            {
                apiQuery.Add("count", count);
            }
            if (offset.HasValue)
            {
                apiQuery.Add("offset", offset);
            }
            if (sort != null)
            {
                apiQuery.Add("sort", sort.Value);
            }
            if (city.HasValue)
            {
                apiQuery.Add("city", city.Value);
            }
            if (country.HasValue)
            {
                apiQuery.Add("country", country.Value);
            }
            if (hometown != null)
            {
                apiQuery.Add("hometown", hometown);
            }
            if (universityCountry.HasValue)
            {
                apiQuery.Add("university_country", universityCountry.Value);
            }
            if (university.HasValue)
            {
                apiQuery.Add("university", university.Value);
            }
            if (universityYear.HasValue)
            {
                apiQuery.Add("university_year", universityYear.Value);
            }
            if (universityFaculty.HasValue)
            {
                apiQuery.Add("university_faculty", universityFaculty.Value);
            }
            if (universityChair.HasValue)
            {
                apiQuery.Add("university_chair", universityChair.Value);
            }
            if (sex != null)
            {
                apiQuery.Add("sex", sex.Value);
            }
            if (status != null)
            {
                apiQuery.Add("status", status.Value);
            }
            if (ageFrom.HasValue)
            {
                apiQuery.Add("age_from", ageFrom.Value);
            }
            if (ageTo.HasValue)
            {
                apiQuery.Add("age_to", ageTo.Value);
            }
            if (birthDay.HasValue)
            {
                apiQuery.Add("birth_day", birthDay.Value);
            }
            if (birthMonth.HasValue)
            {
                apiQuery.Add("birth_month", birthMonth.Value);
            }
            if (birthYear.HasValue)
            {
                apiQuery.Add("birth_year", birthYear.Value);
            }
            if (isOnline.HasValue)
            {
                apiQuery.Add("online", isOnline.Value ? 1 : 0);
            }
            if (hasPhoto.HasValue)
            {
                apiQuery.Add("has_photo", hasPhoto.Value ? 1 : 0);
            }
            if (school.HasValue)
            {
                apiQuery.Add("school", school.Value);
            }
            if (schoolYear.HasValue)
            {
                apiQuery.Add("school_year", schoolYear.Value);
            }
            if (schoolCountry.HasValue)
            {
                apiQuery.Add("school_country", schoolCountry.Value);
            }
            if (schoolCity.HasValue)
            {
                apiQuery.Add("school_city", schoolCity.Value);
            }
            if (schoolClass.HasValue)
            {
                apiQuery.Add("school_class", schoolClass.Value);
            }
            if (religion != null)
            {
                apiQuery.Add("religion", religion);
            }
            if (interests != null)
            {
                apiQuery.Add("interests", interests);
            }
            if (company != null)
            {
                apiQuery.Add("company", company);
            }
            if (position != null)
            {
                apiQuery.Add("position", position);
            }
            if (groupId.HasValue)
            {
                apiQuery.Add("group_id", groupId.Value);
            }
            if (fromList != null)
            {
                apiQuery.Add("from_list", fromList);
            }

            var resp = await _api.Manager.Execute(apiQuery);
            if (_api.Manager.MethodSuccessed)
            {
                if (!resp.ChildNodes.Any())
                {
                    return null;
                }
                var nodes =
                    resp.SelectNodes("items/*");
                return new ListCount<User>(resp.Int("count").Value, nodes.Select(node => new User(node)).ToList());
            }
            return null;
        }

        /// <summary>
        ///     Возвращает список идентификаторов пользователей, которые являются подписчиками пользователя. Идентификаторы
        ///     пользователей в списке отсортированы в порядке убывания времени их добавления.
        /// </summary>
        /// <param name="userId">Идентификатор пользователя. </param>
        /// <param name="nameCase">
        ///     Падеж для склонения имени и фамилии пользователя. Возможные значения: именительный – nom,
        ///     родительный – gen, дательный – dat, винительный – acc, творительный – ins, предложный – abl. По умолчанию nom.
        /// </param>
        /// <param name="count">Количество подписчиков, информацию о которых нужно получить. </param>
        /// <param name="offset">Смещение, необходимое для выборки определенного подмножества подписчиков. </param>
        /// <param name="fields">Список дополнительных полей, которые необходимо вернуть. </param>
        public async Task<ListCount<User>> GetFollowers(int? userId = null, string nameCase = null, int? count = null,
            int? offset = null, string[] fields = null)
        {
            var query = new ApiQuery("users.getFollowers", noAuth: true);
            if (userId != null)
            {
                query.Add("user_id", userId);
            }
            if (nameCase != null)
            {
                query.Add("name_case", nameCase);
            }
            if (count != null)
            {
                query.Add("count", count);
            }
            if (offset != null)
            {
                query.Add("offset", offset);
            }
            if (fields != null)
            {
                query.Add("fields", fields);
            }

            var result = await _api.Manager.Execute(query);
            if (_api.Manager.MethodSuccessed)
            {
                if (!result.ChildNodes.Any())
                {
                    return null;
                }

                var c = result.Int("count");
                var msgsNodes = result.SelectNodes("items/user");
                var res = msgsNodes.Any()
                    ? new ListCount<User>(c.Value, msgsNodes.Select(y => new User(y)).ToList())
                    : null;
                if (res != null) return res;
                msgsNodes = result.SelectNodes("items/user_id");
                res = msgsNodes.Any()
                    ? new ListCount<User>(c.Value, msgsNodes.Select(y => new User { Id = int.Parse(y.String()) }).ToList())
                    : null;
                return res;
            }
            return null;
        }

        /// <summary>
        ///     Позволяет пожаловаться на пользователя.
        /// </summary>
        /// <param name="userId">Идентификатор пользователя, на которого осуществляется жалоба</param>
        /// <param name="type">Тип жалобы</param>
        /// <param name="comment">Комментарий к жалобе на пользователя</param>
        /// <returns>В случае успешной жалобы метод вернет true.</returns>
        public async Task<bool> Report(int userId, UserReportType type, string comment = null)
        {
            var query = new ApiQuery("users.report");
            query.Add("user_id", userId);
            query.Add("type", type.StringValue);
            if (comment != null)
            {
                query.Add("comment", comment);
            }
            var resp = await _api.Manager.Execute(query);
            if (_api.Manager.MethodSuccessed)
            {
                if (!resp.ChildNodes.Any())
                {
                    return false;
                }
                return resp.Bool().Value;
            }
            return false;
        }

        /// <summary>
        ///     Возвращает список идентификаторов пользователей и групп, которые входят в список подписок пользователя.
        /// </summary>
        /// <param name="userId">Идентификатор пользователя, подписки которого необходимо получить. </param>
        public async Task<UsersGroupsIDList> GetSubcriptions(int? userId = null)
        {
            var query = new ApiQuery("users.getSubscriptions", true);
            if (userId != null)
            {
                query.Add("user_id", userId.Value);
            }

            var result = await _api.Manager.Execute(query);
            if (_api.Manager.MethodSuccessed)
            {
                if (!result.ChildNodes.Any())
                {
                    return null;
                }
                var groupsNodes = result.SelectNodes("groups/items/group_id");
                var usersNodes = result.SelectNodes("users/items/user_id");
                var res = new UsersGroupsIDList
                {
                    Groups =
                        new ListCount<int>(result.SelectNode("groups/count").Int().Value,
                            (from ApiNode node in groupsNodes select node.Int().Value).ToList()),
                    Users =
                        new ListCount<int>(result.SelectNode("users/count").Int().Value,
                            (from ApiNode node in usersNodes select node.Int().Value).ToList())
                };
                return res;
            }
            return null;
        }
    }
}